之前在探討模組解析時,不知道大家有沒有注意到 import { b } from “./moduleB”
在查找模組的過程中,除了同層資料夾尋找 moduleB.ts
之外,也會查找moduleB.d.ts
檔案呢?
今天想來探討.d.ts
究竟是什麼?.d.ts 可以稱之為宣告檔案(declaration file),d 就是 declaration 的縮寫,一般 React 專案開發不會遇到宣告檔案,是 JS 轉成 TS 編寫才會遇到的檔案格式。
.d.ts (宣告檔案)其實就是表示主檔案.ts 型別描述的檔案。一般而言,我們會將簡單的型別定義直接寫在主檔案.ts中,遇到較複雜結構才會將特殊的型別定義放在 d.ts 中,另一種狀況則是使用第三方函式庫時。今天的文章將主要著重在第三方函式庫的宣告檔案使用。
在使用 TS 進行開發時,不可避免地有時候會需要引用第三方函式庫,有些函式庫支援 TS ,但有些仍然用純 JS 編寫,這時候如果我們想要在 TS 中使用純 JS 撰寫的函式庫,那該怎麼辦呢?
首先,先來探討那些已經有支援型別的函式庫。對於那些已經支援 TS 的函式庫,我們只需要查找別人已經幫我定義好的第三方宣告檔案即可。在 TS 2.0 版本之後,進行查找官方推薦使用 npm 來管理,舉例來說:要取得 lodash 和 jQuery 函式庫的宣告檔案,只需要打下面的指令,使用 npm 安裝即可
npm install @types/lodash --save
npm install @types/jquery --save
一旦 npm 下載完,就可以在全域或模組使用。宣告檔案將被安裝到node_modules@types\lodash 及 node_modules@types\jQuery 資料夾中。
import * as _ from "lodash";
_.padStart("Hello TypeScript!", 20, " ");
_.padStart("Hello TypeScript!", 20, " ");
在大多數情況下,型別宣告檔案會和第三方函式庫名稱相同,但加上前綴@types/。如果需要,也可以在利用此頁面搜尋需要的宣告檔案。
如果使用的第三方函式庫沒有已經定義好的宣告檔案呢?那就只能自力救濟自己寫了!
如果希望較好的管理第三方函式庫宣告檔案的引用,不希望允許自動全域調用,則可以在 tsconfig.json 中的 typeRoots 和 types 中設定。
{
"compilerOptions": {
"typeRoots": [],
"types" : [
"jquery"
]
}
}
typeRoots 是用來設定 TS 編譯器查找宣告檔案的位置,預設為 ./node_modules/@types
,由於上面僅在 types 中放入 jQuery ,因此即使使用npm install @types/node
安裝了另一個第三方函式庫如 Node.js,也不能在全域使用,除非將 Node.js 加到types中。
一般來說,撰寫宣告檔案的方式取決於函式庫如何使用,需要從函式庫本身的原始碼進行分析,大致可以分成幾種:
目前大部分常見的全域函式庫都以下面的UMD函式庫形式編寫
會看到:
不會看到:
舉例來說可能會見到:
function createGreeting(s) {
return "Hello, " + s;
}
window.createGreeting = function(s) {
return "Hello, " + s;
}
一般來說跟上面的全域函式庫或全域套件類似,但是需要require來啟動效果。
var unused = require("magic-string-time");
//或
require("magic-string-time");
var x = "hello, world";
console.log(x.startsWithHello());
var y = [1, 2, 3];
console.log(y.reverseAndSort());
目前許多常見的函式庫如express、gulp和request都是模組函式庫。
會看到:
很少看到:
舉例來說可能會見到:
var fs = require("fs");
//或
import fs = require("fs");
//或
var someLib = require('someLib');
//或
define(..., ['someLib'], function(someLib) {
...
});
可以作為模組使用,也可以作為全域使用,常見的函式庫例如:Moment.js 、jQuery、lodash 就屬於 UMD 模組。
UMD模組會檢查是否存在模組加載器環境,舉例來說:
(function (root, factory) {
if (typeof define === "function" && define.amd) {
define(["libName"], factory);
} else if (typeof module === "object" && module.exports) {
module.exports = factory(require("libName"));
} else {
root.returnExports = factory(root.libName);
}
}(this, function (b) {
如果看見像上面的 typeof define、typeof window 或 typeof module 這樣的檢查語法,特別是在檔案的上方,那麼極高可能是UMD函式庫。
在官網中針對上面各種不同類型的函式庫也提供了不同的模板供開發者撰寫宣告檔案的參考。
倘若使用的函式庫有相依情況,則需要將相依檔案導入宣告檔案,引入後使用.運算子進行調用
使用/// <reference types="..." />
導入
/// <reference types="someLib" />
function getThing(): someLib.thing;
使用 import 導入
import * as moment from "moment";
function getThing(): moment;
如果是全域函式庫相依UMD模組,則使用/// <reference types
/// <reference types="moment" />
function getThing(): moment;
如果是模組或UMD相依UMD,則使用import
import * as someLib from 'someLib';
注意:不要使用/// <reference去宣告UMD模組的相依性。
今天主要探討了使用宣告檔案的使用情境,介紹了如何載入已定義好的宣告檔案,以及如何判斷函式庫使用方式,以自行撰寫宣告檔案。至於,如何自行撰寫宣告檔案以及發布,則留待明天繼續探討。